{-
    Copyright © 2011 - 2021, Ingo Wechsung
 
    All rights reserved.
 
    Redistribution and use in source and binary forms, with or
    without modification, are permitted provided that the following
    conditions are met:

    -   Redistributions of source code must retain the above copyright
        notice, this list of conditions and the following disclaimer.

    -   Redistributions in binary form must reproduce the above
        copyright notice, this list of conditions and the following
        disclaimer in the documentation and/or other materials provided
        with the distribution. Neither the name of the copyright holder
        nor the names of its contributors may be used to endorse or
        promote products derived from this software without specific
        prior written permission.
 
    *THIS SOFTWARE IS PROVIDED BY THE
    COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
    OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
    USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
    AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
    THE POSSIBILITY OF SUCH DAMAGE.*
-}

{--
    Provides functions and instances for the 'Maybe' type.

    Compatible with the Haskell 2010 _Data.Maybe_ module.

-}

protected package frege.prelude.Maybe
        inline (Maybe.Maybe.>>=, Maybe.Maybe.>>, Maybe.Maybe.pure, Maybe.Maybe.<*>) 
    where

import frege.prelude.PreludeBase
import frege.prelude.PreludeMonad
import frege.prelude.PreludeList
-- import frege.control.Semigroupoid



instance Functor Maybe where
    fmap f Nothing  = Nothing
    fmap f (Just x) = Just (f x)

instance Applicative Maybe where
    pure          = Just
    pa <*> pb     = case pa of
                        Nothing -> Nothing
                        Just f  -> fmap f pb


instance Monad Maybe where
    --- > Nothing >>= _ = Nothing
    --- > Just a  >>= k = k a
    mb >>= k      = case mb of
                        Just a  -> k a
                        Nothing -> Nothing
    a >> b        = a >>= const b
    -- pure          = Just

instance MonadFail Maybe where    
    fail  = const Nothing

instance MonadPlus Maybe where
    mzero = Nothing
    mplus Nothing x = x
    mplus x _ = x

instance ListEmpty Maybe where
    empty        = Nothing
    null Nothing = true
    null _       = false


derive Eq   (Maybe a)
derive Ord  (Maybe a)
-- derive Show Maybe a is in Text

--- @true@ if and only if the argument is a 'Just' value
isJust (Just _) = true
isJust Nothing  = false

--- @true@ if and only if the argument is 'Nothing'
--- This function is preferable over @v == Nothing@ because no 'Eq' constraint is needed.
isNothing Nothing = true
isNothing (Just _) = false

--- @fromMaybe d (Just a)@ returns @a@ and @fromMaybe d Nothing@ returns @d@
fromMaybe d Nothing  = d
fromMaybe d (Just a) = a

--- @unJust Nothing@ is 'undefined' whereas @unJust (Just a)@ is @a@
unJust (Just a) = a
unJust Nothing  = error "unJust Nothing"

--- The 'catMaybes' function takes a list of 'Maybe's and returns a list of all the 'Just' values.
catMaybes xs = [ a | Just a <- xs ]

{--
    The 'mapMaybe' function is a version of 'map' which can throw out elements. 
    In particular, the functional argument returns something of type 'Maybe' _b_. 
    If this is 'Nothing', no element is added on to the result list. 
    If it just 'Just' _b_, then _b_ is included in the result list.
    -}
mapMaybe f xs = catMaybes (map f xs)

--- give the first element of a list or 'Nothing'
listToMaybe xs = listToMaybe xs.toList where      -- reexported from frege.prelude.Maybe
    listToMaybe (a:_) = Just a
    listToMaybe []    = Nothing

--- convert a 'Maybe' to a single element list or an empty list
maybeToList :: Maybe a -> [a]
maybeToList = toList 
